#include "scene.h"

Scene::Scene() : objects(), lights(),texmanager(), ambiantLight(1.0,1.0,1.0), distanceFog(0.0), colorFog(0.0,0.0,0.0)
{

}

Scene::~Scene()
{
    cleanAllScene();
}

Scene::Scene(const Scene &scene) : objects(scene.objects), lights(scene.lights), texmanager(), ambiantLight(0.0,0.0,0.0), distanceFog(0.0), colorFog(0.0,0.0,0.0)
{

}

Scene& Scene::operator=(const Scene &scene)
{
    objects = scene.objects;
    lights = scene.lights;
    ambiantLight = scene.ambiantLight;
    distanceFog = 0.0;
    colorFog    = Color(0.0,0.0,0.0);
    return (*this);
}

void Scene::addObject(SolidObject * sObject)
{
    objects.push_back(sObject);
}

void Scene::addLight(Light * light)
{
    lights.push_back(light);
}

FTexture * Scene::addTexture(const std::string &fileName)
{
    return texmanager.addTexture(fileName);
}


void Scene::setAmbiantLight(const Color &color)
{
    ambiantLight = color;
}

void Scene::setFogParameters(const double &distance, const Color &color)
{
    distanceFog = distance;
    colorFog    = color;
}

void Scene::cleanAllScene()
{
    unsigned int n(0);
    for(; n<objects.size() ; n++)
        if(objects[n])
        {
            delete objects[n];
            objects[n]=0;
        }

    for(n=0 ; n<lights.size() ; n++)
        if(lights[n])
        {
            delete lights[n];
            lights[n]=0;
        }
    objects.clear();
    lights.clear();
}

inline Intersection Scene::nearIntersection(const Ray &ray) const
{
    std::vector<SolidObject *>::const_iterator ptr(objects.begin());
    Intersection res, tmp;
    res.setDistance(1e100);

    for(; (ptr != objects.end()) ; ptr++)
    {
        tmp.setValid(false);
        (*ptr)->intersection(ray,tmp);
        if(  tmp.isValid() && ( tmp < res ) )
            res = tmp;
    }
    return res;
}

inline void Scene::getColorAt(Ray ray, Intersection &inter, Color &res, unsigned int level) const
{
    if( inter.getObject()->isTextured() )
        inter.getObject()->texturing(inter);

    Vector3D n;
    inter.getObject()->getNormalVectorAt(inter,n);
    n.normalize(); //a enlver
    res = inter.getObject()->getColor()*0.1;
    Intersection inter2;//inter pour ombre
    Vector3D l;         //vecteur directeur de la lumiere
    Vector3D r(ray.getDirection() - (n*((2.0)*(n*ray.getDirection())))); // rayon reflechi
    r.normalize();

    double distanceToLight;
    std::vector<Light *>::const_iterator ptr(lights.begin());

    for(; (ptr != lights.end()) ; ptr++)
    {
        l = inter - (**ptr);
        distanceToLight = l.norm()*0.999999;
        l.normalize();
        inter2 = nearIntersection(Ray(**ptr,l));

        if( inter2.getDistance() > distanceToLight )//si la lumiere eclaire ce point
        {
            //lumiere diffuse
            res += ((*ptr)->getColor() * inter.getObject()->getDiffuseColor()) * (fabs(l*n)/lights.size());
            //lumiere speculaire
            if( (l*r) < 0.0 )   //car la lumiere neclaire pas forcement ce que lon voit
                res += ((*ptr)->getColor() * inter.getObject()->getSpecularCoef()  * (pow(-(l*r),inter.getObject()->getSpecularPower())/lights.size()));
        }
    }

    //compute FOG
    if( distanceFog > 0.0 )
    {
        double distanceRatio(inter.getDistance()/distanceFog);
        if( distanceRatio > 1.0 )
            distanceRatio = 1.0;
        res = (res*(1.0-distanceRatio)) + (colorFog*distanceRatio);
    }

    level--;
    Color tmp;
    Point3D origin(inter);

    //now compute reflectivity if object is reflective
    if( inter.getObject()->isReflective() )
    {
        ray.setOrigin(origin + r*0.00001);
        ray.setDirection(r);
        rayTracering(ray,tmp,level);
        res += tmp * inter.getObject()->getReflectiveCoef();
    }
}

void Scene::rayTracering(Ray ray, Color &res, unsigned int level_max) const
{
    if( level_max > 0 )
    {
        res.setColor(0.0,0.0,0.0);
        ray.normalize();

        Intersection inter = nearIntersection(ray);

        //printf("Intersection at [%f ; %f ; %f]\n",inter.getX(), inter.getY(), inter.getZ());
        if(  inter.isValid()  )
            getColorAt(ray,inter,res,level_max);
        else
            res = colorFog;
    }
}
